#include "Application.h"
#include "hzpch.h"

#include "log.h"

// #include "glad/glad.h"

#include "Input.h"
#include "Render/Buffer.h"
#include "Render/Renderer.h"

#include <GLFW/glfw3.h>

namespace Hazel
{

#define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)

Application *Application::s_Instance = nullptr;

Application::Application(const std::string& name)
{
    HZ_PROFILE_FUNC();

    int a = 0;

    if (s_Instance)
    {
        HZ_CORE_ERROR("Application is already exists!");
    }
    s_Instance = this;

    m_Window = Hazel::Scope<Window>(Window::Create(WindowProps(name)));

    m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));

    Renderer::Init();

    m_ImguiLayer = new ImguiLayer();
    PushOverlay(m_ImguiLayer);
}

void HAZEL_API Application::run()
{
    HZ_PROFILE_FUNC();

    while (m_Runing)
    {
        HZ_PROFILE_SCROPE("Run loop");
        float time = (float)glfwGetTime(); // platform::gettime()
        Timestep timestep = time - m_LastFrameTime;
        m_LastFrameTime = time;
        // Renderer::Flush();
        if (!m_Minimized)
        {
            {
                HZ_PROFILE_SCROPE("Layer stack OnUpdate")
                for (Layer *layer : m_layerstack)
                {
                    layer->OnUpdate(timestep);
                }
            }
            m_ImguiLayer->Begin();
            {
                HZ_PROFILE_SCROPE("Layer stack OnImGuiRender")
                for (Layer *layer : m_layerstack)
                {
                    layer->OnImGuiRender();
                }
            }
            m_ImguiLayer->End();
        }

        m_Window->OnUpdate();
    }
}

void Application::OnEvent(Event &e)
{

    EventDispatcher dispatcher(e);
    dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose));
    dispatcher.Dispatch<WindowResizeEvent>(BIND_EVENT_FN(OnWindowResize));
    // HZ_CORE_TRACE("{0}",e);

    // 事件处理需要逆序查找
    for (auto it = m_layerstack.rbegin(); it != m_layerstack.rend(); ++it)
    {
        (*it)->OnEvent(e);
        if (e.IsHandled())
            break;
    }
}

void Application::PushLayer(Layer *layer)
{
    HZ_PROFILE_FUNC();

    m_layerstack.PushLayer(layer);
    layer->OnAttach();
}

void Application::PushOverlay(Layer *overlay)
{
    HZ_PROFILE_FUNC();
    m_layerstack.PushOverlay(overlay);
    overlay->OnAttach();
}

void Application::Close()
{
    m_Runing = false;
}

bool Application::OnWindowClose(WindowCloseEvent &e)
{
    m_Runing = false;
    return true;
}

bool Application::OnWindowResize(WindowResizeEvent &e)
{
    HZ_PROFILE_FUNC();

    if (e.GetWidth() == 0 || e.GetHeight() == 0)
    {
        m_Minimized = true;
        return false;
    }
    m_Minimized = false;
    Renderer::OnWindowResize(e.GetWidth(), e.GetWidth());
    return false;
}

} // namespace Hazel
